In [1]:
class Array:
"Una clase minima para algebra lineal"
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def __repr__(self):
self.repr = ""
for i in range((self.shape[0]-1)):
self.repr += str(self.data[i]) + ',' + '\n'
self.repr = self.repr + str(self.data[self.shape[0]-1])
return "([" + self.repr + "])"
def __str__(self):
self.stru = ""
self.str = ""
for j in range(self.shape[0]-1):
self.str = ""
for i in range(self.shape[1]):
self.str += str(self.data[j][i]) + ' '
self.stru += self.str + '\n'
self.str = ""
for i in range(self.shape[1]):
self.str += str(self.data[self.shape[0]-1][i]) + ' '
self.stru = self.stru + self.str
return "[ " + str(self.stru) +"]"
In [2]:
A = Array([[1,2,3,4],[4,5,6,7]])
In [3]:
A
Out[3]:
In [4]:
print(A)
In [410]:
class Array:
"Una clase minima para algebra lineal"
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def __getitem__(self, idx):
return self.data[idx[0]][idx[1]]
def __setitem__(self,idx,new_value):
self.data[idx[0]][idx[1]] = new_value
In [6]:
A = Array([[1,2,3],[4,5,6]])
In [7]:
A[0,1]
Out[7]:
In [8]:
A[0,1] = 3
In [9]:
A[0,1]
Out[9]:
def zeros(shape):
"Implementame por favor"
[0. for x in range(5)]
crea una lista de 5 ceros.eye(n)
que crea la matriz identidad de $n\times n$ (es decir, la matriz que tiene puros ceros y unos en la diagonal). El nombre eye
es tradicional en software de algebra lineal, aunque no es muy intuitivo.
In [10]:
class Array:
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
self.shape = (len(list_of_rows), len(list_of_rows[0]))
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def zeros(self,r,c):
self.columnas = c
self.filas = r
self.zeros = [[0 for col in range(c)] for row in range(r)]
return self.zeros
def eye(self,n):
self.zeros = [[0 for col in range(n)] for row in range(n)]
for i in range(n):
self.zeros[i][i] = 1
return self.zeros
In [11]:
A = Array([[1,2,3],[4,5,6]])
In [12]:
A.zeros(2,3)
Out[12]:
In [13]:
A.eye(2)
Out[13]:
In [14]:
class Zeros:
def __init__(self,r,c):
self.columnas = c
self.filas = r
self.data = [[0 for col in range(c)] for row in range(r)]
def __repr__(self):
self.repr = ""
for i in range((self.filas-1)):
self.repr += str(self.data[i]) + ',' + '\n'
self.repr = self.repr + str(self.data[self.filas-1])
return "([" + self.repr + "])"
def __str__(self):
self.stru = ""
self.str = ""
for j in range(self.filas-1):
self.str = ""
for i in range(self.columnas):
self.str += str(self.data[j][i]) + ' '
self.stru += self.str + '\n'
self.str = ""
for i in range(self.columnas):
self.str += str(self.data[self.filas-1][i]) + ' '
self.stru = self.stru + self.str
return "[ " + str(self.stru) +"]"
In [15]:
x = Zeros(5,6)
In [16]:
x
Out[16]:
In [17]:
print(x)
In [18]:
class eye:
def __init__(self,n):
self.dim = n
self.data = [[0 for col in range(n)] for row in range(n)]
for i in range(n):
self.data[i][i] = 1
def __repr__(self):
self.repr = ""
for i in range((self.dim-1)):
self.repr += str(self.data[i]) + ',' + '\n'
self.repr = self.repr + str(self.data[self.dim-1])
return "([" + self.repr + "])"
def __str__(self):
self.stru = ""
self.str = ""
for j in range(self.dim-1):
self.str = ""
for i in range(self.dim):
self.str += str(self.data[j][i]) + ' '
self.stru += self.str + '\n'
self.str = ""
for i in range(self.dim):
self.str += str(self.data[self.dim-1][i]) + ' '
self.stru = self.stru + self.str
return "[ " + str(self.stru) +"]"
In [19]:
y = eye(2)
In [20]:
y
Out[20]:
In [21]:
print(y)
In [22]:
class Array:
"Una clase minima para algebra lineal"
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
self.shape = (len(list_of_rows), len(list_of_rows[0]))
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def transpose(self):
self.fila = self.shape[0]
self.columna = self.shape[1]
self.row = [[0 for col in range(self.fila)] for row in range(self.columna)]
for j in range(self.columna):
for i in range(self.fila):
self.row[j][i] = self.data[i][j]
return self.row
In [23]:
A = Array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
In [24]:
A.transpose()
Out[24]:
A + 1
tiene sentido para un Array A
, sin embargo la expresion inversa falla, por ejemplo 1 + Array([[1,2], [3,4]])
__radd__
para resolver este problema.__sub__
similar a la suma para poder calcular expresiones como A - B para A y B arrays o numeros.
In [25]:
class Array:
"Una clase minima para algebra lineal"
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def __add__(self, other):
if isinstance(other, Array):
if self.shape != other.shape:
raise Exception("Las dimensiones son distintas!")
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] + other.data[r][c]
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] + other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
"Parte 1"
__radd__ = __add__
"Parte 2"
def __sub__(self, other):
if isinstance(other, Array):
if self.shape != other.shape:
raise Exception("Las dimensiones son distintas!")
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] - other.data[r][c]
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] - other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
def __rsub__(self, other):
if isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = other - self.data[r][c]
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
In [26]:
A = Array([[1,2,3],[4,5,6]])
In [27]:
B = Array([[4,5,6],[7,8,9]])
In [28]:
C = A + B
In [29]:
C.data
Out[29]:
In [30]:
D = A + 2
In [31]:
D.data
Out[31]:
In [32]:
E = 3 + B
In [33]:
E.data
Out[33]:
In [34]:
F = A - B
In [35]:
F.data
Out[35]:
In [36]:
G = B - 3
In [37]:
G.data
Out[37]:
In [38]:
H = 10 - A
In [39]:
H.data
Out[39]:
In [10]:
class Array:
def __init__(self, list_of_rows):
"Constructor y validador"
# obtener dimensiones
self.data = list_of_rows
nrow = len(list_of_rows)
# ___caso vector: redimensionar correctamente
if not isinstance(list_of_rows[0], list):
nrow = 1
self.data = [[x] for x in list_of_rows]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def __mul__(self, other):
if isinstance(other, Array):
if self.shape[1] != other.shape[0]:
raise Exception("Las dimensiones no coinciden!")
rowsarr = self.shape[0]
coincidir = self.shape[1]
colsoth = other.shape[1]
val = 0
newArray = Array([[0. for c in range(colsoth)] for r in range(rowsarr)])
for r in range(rowsarr):
for c in range(colsoth):
val = 0
for i in range(coincidir):
val += self.data[r][i] * other.data[i][c]
newArray.data[r][c] = val
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] * other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
__rmul__ = __mul__
In [11]:
A = Array([[1,2],[4,5],[7,8]])
In [12]:
B = Array([[4,5,6],[7,8,9]])
In [14]:
C = A * B
In [15]:
C.data
Out[15]:
In [17]:
D = A * 2
In [18]:
D.data
Out[18]:
In [21]:
E = -2 * B
In [22]:
E.data
Out[22]:
EJERCICIOS 7-10
Implementa una funcion forward_subs que resuelva sistemas de ecuaciones de la forma $Lx = y$ con $L$ triangular inferior y $y$ cualquier Vector o Array de una columna.
Implementa una funcion backward_subs que resuelva sistemas $Ux = y$ con $U$ triangular superior y y Vector o Array de una columna.
Implementa una funcion LU que reciba un Array $A$ y devuelva 3 arrays $L$,$U$ y $P$ tales que $PA = LU$ con $L$ trangular inferior, $U$ triangular superior y $P$ matriz de permutacion.
Implementa una funcion lu_linsolve que resuelva cualquier sistema de ecuaciones Ax = y con A un Array y y un Vector o Array de una columna. La solución es muy sencilla usando la descomposición LU de los puntos anteriores.
In [558]:
class Array:
def __init__(self, L):
"Constructor y validador"
# obtener dimensiones
self.data = L
nrow = len(L)
# ___caso vector: redimensionar correctamente
if not isinstance(L[0], list):
nrow = 1
self.data = [[x] for x in L]
# ahora las columnas deben estar bien aunque sea un vector
ncol = len(self.data[0])
self.shape = (nrow, ncol)
# validar tamano correcto de filas
if any([len(r) != ncol for r in self.data]):
raise Exception("Las filas deben ser del mismo tamano")
def __repr__(self):
self.repr = ""
for i in range((self.shape[0]-1)):
self.repr += str(self.data[i]) + ',' + '\n'
self.repr = self.repr + str(self.data[self.shape[0]-1])
return "([" + self.repr + "])"
def __str__(self):
self.stru = ""
self.str = ""
for j in range(self.shape[0]-1):
self.str = ""
for i in range(self.shape[1]):
self.str += str(self.data[j][i]) + ' '
self.stru += self.str + '\n'
self.str = ""
for i in range(self.shape[1]):
self.str += str(self.data[self.shape[0]-1][i]) + ' '
self.stru = self.stru + self.str
return "[ " + str(self.stru) +"]"
def __add__(self, other):
if isinstance(other, Array):
if self.shape != other.shape:
raise Exception("Las dimensiones son distintas!")
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] + other.data[r][c]
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] + other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
__radd__ = __add__
def __sub__(self, other):
if isinstance(other, Array):
if self.shape != other.shape:
raise Exception("Las dimensiones son distintas!")
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] - other.data[r][c]
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] - other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
def __rsub__(self, other):
if isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = other - self.data[r][c]
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
def __mul__(self, other):
if isinstance(other, Array):
if self.shape[1] != other.shape[0]:
raise Exception("Las dimensiones no coinciden!")
rowsarr = self.shape[0]
coincidir = self.shape[1]
colsoth = other.shape[1]
val = 0
newArray = Array([[0. for c in range(colsoth)] for r in range(rowsarr)])
for r in range(rowsarr):
for c in range(colsoth):
val = 0
for i in range(coincidir):
val += self.data[r][i] * other.data[i][c]
newArray.data[r][c] = val
return newArray
elif isinstance(2, (int, float, complex)): # en caso de que el lado derecho sea solo un numero
rows, cols = self.shape
newArray = Array([[0. for c in range(cols)] for r in range(rows)])
for r in range(rows):
for c in range(cols):
newArray.data[r][c] = self.data[r][c] * other
return newArray
else:
return NotImplemented # es un tipo de error particular usado en estos metodos
__rmul__ = __mul__
def __getitem__(self, idx):
return self.data[idx[0]][idx[1]]
def __setitem__(self,idx,new_value):
self.data[idx[0]][idx[1]] = new_value
# Ejercicio 7
def forward_subs(self, y):
L = self.data
nrow = len(L)
ncol = len(L[0])
if (nrow != ncol):
raise Exception("La matriz debe ser cuadrada")
for i in range(0,nrow):
for j in range(0,ncol):
if i < j:
if self.data[i][j] != 0:
raise Exception("No es matriz triangular inferior")
x = []
for i in range(nrow):
x.append(y[i,0])
for j in range(i):
x[i] -= (L[i][j] * x[j])
x[i] /= L[i][i]
return x
# Ejercicio 8
def backward_subs(self, y):
U = self.data
nrow = len(U)
ncol = len(U[0])
if (nrow != ncol):
raise Exception("La matriz debe ser cuadrada")
for i in range(0,nrow):
for j in range(0,ncol):
if i > j:
if self.data[i][j] != 0:
raise Exception("No es matriz triangular superior")
x = [0 for k in range(nrow)]
x[nrow - 1] = y[nrow - 1,0] / U[nrow - 1][nrow - 1]
for i in range(nrow - 2,-1,-1):
m = y[i,0]
for j in range(i + 1, nrow):
m += x[i] - U[i][j] * x[j]
x[i] = m / U[i][i]
return x
# Ejercicio 9
def decompLU(self):
A = self.data
nrow = len(A)
ncol = len(A[0])
P = [[0 for col in range(ncol)] for row in range(nrow)]
L = [[0 for col in range(ncol)] for row in range(nrow)]
U = [[0 for col in range(ncol)] for row in range(nrow)]
# P
for i in range(nrow):
P[i][i]= 1
for k in range(nrow):
r = max(range(k,nrow),key = lambda i: abs(A[i][k]))
if k != r:
P[k], P[r] = P[r], P[k]
P = Array(P)
PA = P * Array(A)
for j in range(nrow):
L[j][j] = 1
for i in range(j + 1):
s1 = sum(L[i][k]*U[k][j] for k in range(i))
U[i][j] = PA[i,j] - s1
for i in range(j,nrow):
s2 = sum(U[k][j]*L[i][k] for k in range(j))
L[i][j] = (PA[i,j] - s2) / U[j][j]
L = Array(L)
U = Array(U)
return L,U,P
# Ejercicio 10
def lu_linsolve(self,b):
A = Array(self.data)
L = A.decompLU()[0]
U = A.decompLU()[1]
P = A.decompLU()[2]
y = L.forward_subs(b)
nrow = A.shape[0]
y1 = b
for i in range(nrow):
y1[i,0] = y[i]
x = U.backward_subs(y1)
x1 = b
for i in range(nrow):
x1[i,0] = x[i]
return x
In [559]:
# Triangular Inferior
# Metodo Forward
B = Array([[3,0,0,0],[34,12,0,0],[56,12,123,0],[12,20,540,11]])
B
Out[559]:
In [560]:
B.forward_subs(Array([[23],[20],[35],[46]]))
Out[560]:
In [561]:
# Triangular Superior
# Metodo Backward
C = Array([[3,1,4,6],[0,1,34,12],[0,0,123,12],[0,0,0,11]])
C
Out[561]:
In [562]:
C.backward_subs(Array([[23],[20],[35],[46]]))
Out[562]:
In [563]:
# Descomposicion PA = LU
A = Array([[1,1,1,4],[20,1,5,-11],[5,-4,3,2],[20,30,-12,11]])
In [564]:
LUP = A.decompLU()
L = LUP[0]
L
Out[564]:
In [565]:
U = LUP[1]
U
Out[565]:
In [566]:
P = LUP[2]
P
Out[566]:
In [567]:
A = Array([[1,1,1,4],[20,1,5,-11],[5,-4,3,2],[20,30,-12,11]])
b = Array([[1],[2],[3],[4]])
x = A.lu_linsolve(b)
# Solucion de PAx = b
x
Out[567]: